package io.jsen.meta.diff.obj.complex;

import io.jsen.meta.diff.feature.IndexFeature;
import io.jsen.meta.diff.field.MetaField;
import io.jsen.meta.diff.obj.MetaObject;
import io.jsen.meta.diff.obj.abs.AbstractComplex;
import io.jsen.meta.diff.obj.special.MetaNull;
import io.jsen.meta.diff.support.MetaModifier;
import io.jsen.meta.diff.utils.Md5Util;
import lombok.EqualsAndHashCode;

import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

/**
 * @author jsen
 * @date 2021/4/6
 */
@EqualsAndHashCode(callSuper = true)
public class MetaComplex extends AbstractComplex<MetaComplex, Map<MetaField, MetaObject<?, ?>>> {

    public MetaComplex(MetaField field, Map<MetaField, MetaObject<?, ?>> val) {
        super(field, val);
        this.index = createIndex();
        this.modifier = MetaModifier.COMPLEX;
        this.val.forEach((f, metaObject) -> metaObject.getMetaRelation().setParent(this));
    }

    @Override
    protected int calState(MetaComplex obj) {
        val.forEach((k, v) -> v.doDiff(obj.val.get(k)));

        return calCollectionState(val.values());
    }

    @SuppressWarnings("all")
    private String createIndex() {
        IndexFeature indexFeature = getMetaRelation().getMetaField().getIndexFeature();
        if (indexFeature != null) {
            return indexFeature.buildIndex(val);
        }
        TreeMap<String, String> treeMap = new TreeMap<>();
        val.forEach((field, metaObj) -> {
            if (field.isMetaUniqueKey()) {
                treeMap.put(field.getFieldDefine().getField().getName(), metaObj.getIndex());
            }
        });
        if (treeMap.isEmpty()) {
            val.forEach((field, metaObj) -> {
                treeMap.put(field.getFieldDefine().getField().getName(), metaObj.getIndex());
            });
        }
        StringBuilder builder = new StringBuilder();
        for (String key : treeMap.keySet()) {
            Object val = treeMap.get(key);
            if (val == null) {
                builder.append(UUID.randomUUID().toString()).append("#");
            } else {
                builder.append(val).append("#");
            }
        }
        return Md5Util.md5(builder.toString());
    }

    @Override
    @SuppressWarnings("rawtypes")
    public MetaObject doDiff(MetaObject obj) {
        super.doDiff(obj);
        if (obj instanceof MetaNull) {
            val.forEach((field, metaObject) -> metaObject.doDiff(new MetaNull(
                    new MetaField(metaObject.getMetaRelation().getMetaField().getFieldDefine(),
                            metaObject.getMetaRelation().getMetaField().getKey())
            )));
        }
        return this;
    }

}
